package com.smile.frame.websocket.netty.service.handler;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.smile.frame.websocket.netty.service.dto.ClientInfoDTO;
import com.smile.frame.websocket.netty.service.dto.WebsocketMessageDTO;
import com.smile.frame.websocket.netty.service.enums.CmdEnum;
import com.smile.frame.websocket.netty.service.repository.ChannelRepository;
import com.smile.frame.websocket.netty.service.repository.ClientRepository;
import com.smile.frame.websocket.netty.service.util.BuilderMessageUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 登录认证 handler
 *
 * @author LGC
 * @ChannelHandler.Sharable 保证处理器，在整个生命周期中就是以单例的形式存在
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class LoginAuthHandler extends ChannelInboundHandlerAdapter {
    /**
     * 读取请求信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("令牌验证 token auth");
        // 判断消息实例
        if (!(msg instanceof TextWebSocketFrame)) {
            super.channelRead(ctx, msg);
            return;
        }
        TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) msg;
        String channelId = ctx.channel().id().asLongText();
        // 消息内容解析
        WebsocketMessageDTO websocketMessageDTO = JSONUtil.toBean(textWebSocketFrame.text(), WebsocketMessageDTO.class);

        ClientInfoDTO clientInfo = websocketMessageDTO.getClientInfo();
        // 客户端信息为空
        if (clientInfo == null) {
            if (ctx.channel().isActive()) {
                TextWebSocketFrame message = BuilderMessageUtil.buildStringMessage(null, CmdEnum.AUTH, "fail");
                ctx.writeAndFlush(message);
            }
            log.info("客户端信息为空");
            return;
        }
        // token 验证
        if (StrUtil.isBlank(clientInfo.getToken())) {
            if (ctx.channel().isActive()) {
                TextWebSocketFrame message = BuilderMessageUtil.buildStringMessage(clientInfo, CmdEnum.AUTH, "fail");
                ctx.writeAndFlush(message);
            }
            log.info("客户端token为空");
            return;
        }
        //todo token验证


        // auth 分配clientId
        if (websocketMessageDTO.getCmd().equals(CmdEnum.AUTH.getCode())) {
            // 存储通道
            if (!ChannelRepository.containsKey(channelId)) {
                log.info("存储channel");
                ChannelRepository.putChannel(ctx.channel());
            }
            // 存储client
            String clientId = "";
            if (!ClientRepository.containsKey(channelId)) {
                // 随机生成clientId
                log.info("存储client");
                clientId = IdUtil.fastUUID();
                ClientRepository.putClient(channelId, clientId);
            } else {
                clientId = ClientRepository.getClient(channelId);
            }
            clientInfo.setClientId(clientId);
            // 认证通过
            ctx.writeAndFlush(BuilderMessageUtil.buildStringMessage(clientInfo, CmdEnum.AUTH, "success"));
        } else {
            super.channelRead(ctx, msg);
        }

    }

    /**
     * 添加一个客户端
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        log.info("一个客户端请求Channel:{},RemoteAddress:{},ThreadName:{}", channel.id().asLongText(), channel.remoteAddress(), Thread.currentThread().getName());
    }

    /**
     * 客户端掉线时的操作
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        String channelId = channel.id().asLongText();
        //移除通信过的channel
        ChannelRepository.removeChannel(channelId);
        //移除和用户绑定的channel
        ClientRepository.removeClient(channelId);
        log.info("一个客户端移除Channel:{},RemoteAddress:{},ThreadName:{}", channelId, channel.remoteAddress(), Thread.currentThread().getName());
        //关闭长连接
        ctx.close();
    }

}
